﻿using MiniFox.Components;
using MiniFox.Mail.Configurations;
using System.Net;
using System.Net.Mail;

namespace MiniFox.Mail
{
    /// <summary>
    /// 
    /// </summary>
    public class MailService : IMailService
    {
        string _name;
        IMailAccount _mailAccount;

        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<MailSentEventArgs> MailSent;
        /// <summary>
        /// 
        /// </summary>
        public event EventHandler<MailErrorEventArgs> MailError;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        protected MailService(string name)
        {
            _name = name;
        }

        #region 
        /// <summary>
        /// 
        /// </summary>
        [AutoConfigure]
        protected MailingConfiguration MailingConfiguration
        {
            get;
            set;
        }

        #endregion

        #region
        /// <summary>
        /// 默认配置化，可重写为DB持久化
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        protected virtual IMailAccount CreateMailAccount(string name)
        {
            var mailAccount = MailingConfiguration.MailAccounts[name];
            return mailAccount;
        }
        /// <summary>
        /// 
        /// </summary>
        protected IMailAccount MailAccount
        {
            get
            {
                if (_mailAccount == null)
                {
                    _mailAccount = CreateMailAccount(_name);
                }
                return _mailAccount;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        private SmtpClient CreateSmtpClient()
        {
            if (this.MailAccount == null)
                throw new MiniFox.Exceptions.SystemException(Resources.CommonMessages.EXPTN_MailAccountUnset);
            SmtpClient client = new SmtpClient(MailAccount.SmtpServer, MailAccount.Port);
            client.EnableSsl = MailAccount.EnableSSL;
            client.UseDefaultCredentials = false;
            client.Timeout = MailAccount.SendTimeout;
            client.Credentials = new NetworkCredential(MailAccount.Account, MailAccount.Password);
            client.DeliveryMethod = SmtpDeliveryMethod.Network;
            return client;

        }
        /// <summary>
        /// 
        /// </summary>
        public bool EnableAsync
        {
            get
            {
                return this.MailAccount.EnableAsync;
            }
        }
        #endregion


        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnMailSent(MailSentEventArgs e)
        {
            if (this.MailSent != null)
                this.MailSent(this, e);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        private void OnError(MailErrorEventArgs e)
        {
            if (this.MailError != null)
                this.MailError(this, e);
            if (!e.IgnoreError)
            {
                throw e.Error;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="toList"></param>
        /// <param name="ccList"></param>
        /// <param name="attachments"></param>
        /// <returns></returns>
        protected virtual MailMessage CreateMailMessage(string title, string body, bool isHtmlBody, IEnumerable<MailAddress> toList, IEnumerable<MailAddress> ccList, IEnumerable<Attachment> attachments)
        {
            MailMessage msg = new MailMessage();
            msg.Subject = title;
            msg.Body = body;
            msg.IsBodyHtml = isHtmlBody;
            msg.From = new MailAddress(MailAccount.DisplayAccount, MailAccount.DisplayName);
            foreach (var to in toList)
                msg.To.Add(to);
            if (ccList != null && ccList.Count() > 0)
                foreach (var cc in ccList)
                    msg.CC.Add(cc);
            if (attachments != null && attachments.Count() > 0)
                foreach (var attachment in attachments)
                    msg.Attachments.Add(attachment);
            try
            {
                string cclist = MailAccount.CCList;
                if (!string.IsNullOrWhiteSpace(cclist))
                    msg.CC.Add(cclist);

                string bcclist = MailAccount.BCCList;
                if (!string.IsNullOrWhiteSpace(bcclist))
                    msg.Bcc.Add(MailAccount.BCCList);

            }
            finally
            {
            }
            return msg;
        }

        #region send sync
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="to"></param>
        public void SendMail(string title, string body, string[] to)
        {
            SendMail(title, body, true, to);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="to"></param>
        public virtual void SendMail(string title, string body, bool isHtmlBody, string[] to)
        {
            SendMail(title, body, isHtmlBody, to, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="to"></param>
        /// <param name="cc"></param>
        public virtual void SendMail(string title, string body, bool isHtmlBody, string[] to, string[] cc)
        {
            SendMail(title, body, isHtmlBody, to, cc, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="to"></param>
        /// <param name="cc"></param>
        /// <param name="attachments"></param>
        public virtual void SendMail(string title, string body, bool isHtmlBody, string[] to, string[] cc, params Attachment[] attachments)
        {
            var receivers = new List<MailAddress>();
            foreach (var receiver in to)
            {
                receivers.Add(new MailAddress(receiver));
            }
            var cclist = new List<MailAddress>();
            if (cc != null && cc.Length > 0)
            {
                foreach (var address in cc)
                {
                    cclist.Add(new MailAddress(address));
                }
            }
            this.SendMail(title, body, isHtmlBody, receivers, cclist, attachments);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="toList"></param>
        /// <param name="ccList"></param>
        /// <param name="attachments"></param>
        public virtual void SendMail(string title, string body, bool isHtmlBody, IEnumerable<MailAddress> toList, IEnumerable<MailAddress> ccList, params Attachment[] attachments)
        {
            using (MailMessage msg = CreateMailMessage(title, body, isHtmlBody, toList, ccList, attachments))
            {
                this.SendMail(msg);
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="msg"></param>
        protected virtual void SendMail(MailMessage msg)
        {
            try
            {
                using (SmtpClient client = CreateSmtpClient())
                {
                    client.Send(msg);
                    OnMailSent(new MailSentEventArgs(this.MailAccount, msg, false));
                }
            }
            catch (Exception ex)
            {
                OnError(new MailErrorEventArgs(this.MailAccount, msg, false, ex));
            }
        }

        #endregion

        #region async
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="to"></param>
        public Task SendMailAsync(string title, string body, string[] to)
        {
            return SendMailAsync(title, body, true, to);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="to"></param>
        public virtual Task SendMailAsync(string title, string body, bool isHtmlBody, string[] to)
        {
            return SendMailAsync(title, body, isHtmlBody, to, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="to"></param>
        /// <param name="cc"></param>
        public virtual Task SendMailAsync(string title, string body, bool isHtmlBody, string[] to, string[] cc)
        {
            return SendMailAsync(title, body, isHtmlBody, to, cc, null);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="to"></param>
        /// <param name="cc"></param>
        /// <param name="attachments"></param>
        public virtual Task SendMailAsync(string title, string body, bool isHtmlBody, string[] to, string[] cc, params Attachment[] attachments)
        {
            var receivers = new List<MailAddress>();
            foreach (var receiver in to)
            {
                receivers.Add(new MailAddress(receiver));
            }
            var cclist = new List<MailAddress>();
            if (cc != null && cc.Length > 0)
            {
                foreach (var address in cc)
                {
                    cclist.Add(new MailAddress(address));
                }
            }
            return this.SendMailAsync(title, body, isHtmlBody, receivers, cclist, attachments);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="title"></param>
        /// <param name="body"></param>
        /// <param name="isHtmlBody"></param>
        /// <param name="toList"></param>
        /// <param name="ccList"></param>
        /// <param name="attachments"></param>
        public async virtual Task SendMailAsync(string title, string body, bool isHtmlBody, IEnumerable<MailAddress> toList, IEnumerable<MailAddress> ccList, params Attachment[] attachments)
        {
            using (MailMessage msg = CreateMailMessage(title, body, isHtmlBody, toList, ccList, attachments))
            {
                await this.SendMailAsync(msg);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="msg"></param>
        protected async virtual Task SendMailAsync(MailMessage msg)
        {
            try
            {
                using (SmtpClient client = CreateSmtpClient())
                {
                    await client.SendMailAsync(msg);
                    OnMailSent(new MailSentEventArgs(this.MailAccount, msg, true));
                }
            }
            catch (Exception ex)
            {
                OnError(new MailErrorEventArgs(this.MailAccount, msg, true, ex));
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="msg"></param>
        protected virtual void SendAsync(MailMessage msg)
        {
            try
            {
                SmtpClient client = CreateSmtpClient();
                client.SendCompleted += (sender, e) =>
                {
                    if (e.Error == null)
                    {
                        OnMailSent(new MailSentEventArgs(this.MailAccount, msg, true, e.Cancelled));
                    }
                    else
                    {
                        OnError(new MailErrorEventArgs(this.MailAccount, msg, true, e.Error));
                    }

                    //SmtpClient smtp = e.UserState as SmtpClient;
                    //if (smtp != null)
                    //    smtp.Dispose();
                    client.Dispose();
                };
                client.SendAsync(msg, null);

            }
            catch (Exception ex)
            {
                OnError(new MailErrorEventArgs(this.MailAccount, msg, true, ex));
            }
        }
        #endregion
    }
}
